package com.ccloud.common.im.handler;

import com.alibaba.fastjson.JSON;
import com.ccloud.common.im.constant.CIMConstant;
import com.ccloud.common.im.model.CIMMsg;
import com.ccloud.common.im.model.CIMSession;
import com.ccloud.common.im.model.HeartbeatRequest;
import com.ccloud.common.im.model.HeartbeatResponse;
import com.ccloud.common.im.util.MessageUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通用处理类
 *
 * @author chentl
 * @version V1.0
 * @project bigdata -cloud
 * @title CommonChannelEventHandler
 * @package com.ccloud.common.im.handler
 * @date 2022 /8/3 10:15 上午
 * @copyright: 武汉大数据产业发展有限公司 All rights reserved.
 */
@Slf4j
@ChannelHandler.Sharable
public class CommonChannelEventHandler extends SimpleChannelInboundHandler<Object> {


    private final CIMRequestHandler outerRequestHandler;

    /**
     * @Description 内置特殊处理handler
     * @Author chentl
     * @Create: 2022/8/3 10:19 上午
     */
    private final HashMap<String, CIMRequestHandler> innerHandlerMap;

    /**
     * @description 线程安全的所有频道组 @author chentl @date 2020/9/7 3:42 下午
     **/
    private final ConcurrentHashMap<String, Channel> channelGroup;


    /**
     * Instantiates a new Common channel event handler.
     *
     * @param outerRequestHandler the outer request handler
     * @param innerHandlerMap     the inner handler map
     * @param channelGroup        the channel group
     */
    public CommonChannelEventHandler(CIMRequestHandler outerRequestHandler, HashMap<String, CIMRequestHandler> innerHandlerMap, ConcurrentHashMap<String, Channel> channelGroup) {
        this.outerRequestHandler = outerRequestHandler;
        this.innerHandlerMap = innerHandlerMap;
        this.channelGroup = channelGroup;
    }

    /**
     * 心跳响应 超时为15秒
     */
    public static final int PONG_TIME_OUT = 15;


    /**
     * @return void
     * @author chentl
     * @description 消息入口
     * @date 2020/9/6 4:49 下午
     * @params [ctx, data]
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object data) {
        //心跳响应忽略
        if (data instanceof HeartbeatResponse) {
            return;
        }
        CIMSession session = new CIMSession(ctx.channel());
        TextWebSocketFrame msg = (TextWebSocketFrame) data;

        CIMMsg body = MessageUtils.convertMsg(msg.text());
        if (Objects.isNull(body)) {
            log.error("[cim] 消息格式异常，无法解析。原始消息:{}", JSON.toJSONString(body));
            return;
        }

        CIMRequestHandler handler = innerHandlerMap.get(body.getType());

        if (handler != null) {
            handler.process(session, body);
            return;
        }
        log.info("[server]收到客户端消息:{}", msg.text());
        outerRequestHandler.process(session, body);

        //TODO 传统的http接入 后续扩展
        if (data instanceof FullHttpRequest) {
            log.info("传统的http消息接入");
        }
    }

    /**
     * @return void
     * @author chentl
     * @description Channel失活
     * @date 2020/9/6 4:50 下午
     * @params [ctx]
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        channelGroup.remove(ctx.channel().id().asShortText());

        CIMSession session = new CIMSession(ctx.channel());
        CIMMsg cimMsg = new CIMMsg();
        cimMsg.setType(CIMConstant.CLIENT_CONNECT_CLOSED);
        outerRequestHandler.process(session, cimMsg);

    }

    /**
     * @return void
     * @author chentl
     * @description Channel激活
     * @date 2020/9/6 4:50 下午
     * @params [ctx]
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        log.info("[server]新连接 {} 加入!", ctx.channel().id());
        channelGroup.put(ctx.channel().id().asShortText(), ctx.channel());
        log.info("[server]当前在线人数:" + channelGroup.size());
    }

    /**
     * @return void
     * @author chentl
     * @description 加入新连接时触发
     * @date 2020/1/2 14:39
     * @params [ctx]
     * @since JDK 1.7
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
    }

    /**
     * @return void
     * @author chentl
     * @description 连接断开
     * @date 2020/1/2 14:40
     * @params [ctx]
     * @since JDK 1.7
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        log.info("[server]连接 {} 现已断开", ctx.channel().id());
        //TODO 从房间中移除一个连接时触发
        log.info("[server]当前在线人数:" + channelGroup.size());

    }

    /**
     * @return void
     * @author chentl
     * @description 连接异常时需要及时关闭相关资源
     * @date 2020/1/2 14:48
     * @params [ctx, cause]
     * @since JDK 1.7
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("[server]连接websocket异常" + cause.toString());
        ctx.close();
        ctx.channel().close();
    }

    /**
     * @return void
     * @author chentl
     * @description 挤一挤管道中的消息
     * @date 2020/1/2 14:48
     * @params [ctx]
     * @since JDK 1.7
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    /**
     * @return void
     * @author chentl
     * @description Socket事件触发
     * @date 2020/9/6 4:51 下午
     * @params [ctx, evt]
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {

        if (!(evt instanceof IdleStateEvent)) {
            return;
        }

        IdleStateEvent idleEvent = (IdleStateEvent) evt;

        if (idleEvent.state().equals(IdleState.WRITER_IDLE)) {
            String heartPack = HeartbeatRequest.getInstance().toJsonString();
            ctx.channel().attr(AttributeKey.valueOf(CIMConstant.HEARTBEAT_KEY)).set(System.currentTimeMillis());
            ctx.channel().writeAndFlush(new TextWebSocketFrame(heartPack));
            log.info("[server nId:{}]发送服务端心跳:{} ", ctx.channel().id(), HeartbeatRequest.getInstance().getContent());
        }

        /**
         * 如果心跳请求发出10秒内没收到响应，则关闭连接
         */
        if (idleEvent.state().equals(IdleState.READER_IDLE)) {

            Long lastTime = (Long) ctx.channel().attr(AttributeKey.valueOf(CIMConstant.HEARTBEAT_KEY)).get();
            if (lastTime != null && System.currentTimeMillis() - lastTime >= PONG_TIME_OUT) {
                ctx.channel().close();
            }

            ctx.channel().attr(AttributeKey.valueOf(CIMConstant.HEARTBEAT_KEY)).set(null);
        }
    }

}